// FLTK implementation of an icon view widget
// Copyright 2005 (C) Ralph Thomas

#include <view/icon.h>
#include <fltk/Widget.h>
#include <fltk/Button.h>
#include <fltk/events.h>
#include <toolkit/fltk/fltkIcon.h>
#include <iostream>
#include <binder.hpp>

namespace view {
	//
	/// The iconView class wraps an fltk::Widget with an interface which
	/// works with ASL.
	//
	class iconView {
		//
		/// Simple class to draw the icon with clipping and scaling.
		//
		class iconWidget : public fltk::Widget {
		  public:
			//
			/// Create a new iconWidget using the given URL for the
			/// drag model.
			///
			/// \param	x		the x-coordinate for this widget.
			/// \param	y		the y-coordinate for this widget.
			/// \param	w		the width of this widget.
			/// \param	h		the height of this widget.
			/// \param	ch		the label of this widget.
			//
			iconWidget( int x, int y, int w, int h, const char* ch )
				: fltk::Widget( x, y, w, h, ch ) {}
			virtual ~iconWidget() {}
			virtual void draw() {
				draw_background();
				draw_box();
				fltk::Rectangle r( 0, 0, w(), h() );
				if( image() ) image()->_draw( r );
			}
		};
		fltk::Widget*			_widget; ///< Drawing widget.
		toolkit::fltkIcon::iconSize	_size;   ///< The icon size.
		adobe::binder_helper_t		_binder; ///< Model binder.
		//
		// void _update( const adobe::value_t& value )
		//
		/// This function gets called when the value of the icon in the
		/// sheet changes. It draws the new icon in place of the old
		/// one.
		///
		/// \param	value	the new icon to draw.
		//
		void _update( const adobe::value_t& value ) {
			try {
				const toolkit::icon* icon( 0 );
				const toolkit::fltkIcon* ficon( 0 );
				//
				// Try to get the icon from the given value.
				//
				value.get( icon );
				ficon = dynamic_cast<const toolkit::fltkIcon*>(
					icon );
				//
				// If we have an icon then tell our widget to
				// draw it.
				//
				if( ficon )
					_widget->image( ficon->get( _size ) );
				else _widget->image( 0 );
			} catch( ... ) {
				//
				// We probably couldn't get any icon. Blank
				// out the image.
				//
				_widget->image( 0 );
			}
			//
			// Force the widget to redraw.
			//
			_widget->redraw();
		}
	  public:
		//
		// iconView( ... )
		//
		/// This constructor will create and initialize a new icon view
		/// widget, assuming that the given parameters (bind, size) are
		/// correct.
		///
		/// \param	parameters	parameters to create the new
		///				view.
		/// \param	token		the factory token to create
		///				against.
		/// \param	parent		the parent widget to insert
		///				into.
		//
		iconView( const adobe::dictionary_t& parameters,
			const adobe::factory_token_t& token,
			const adobe::widget_node_t& parent )
		: _widget( 0 ), _size( toolkit::fltkIcon::k48x48 ),
		  _binder( token.sheet_m ) {
			adobe::value_t bind =
				parameters.at( adobe::static_name_t( "bind" ) );
			//
			// Determine the size which we should use for the icon.
			//
			int size( 48 );
			parameters.get<int>( adobe::static_name_t( "size" ),
				size );
			if( size == 16 ) _size = toolkit::fltkIcon::k16x16;
			else if( size == 22 ) _size = toolkit::fltkIcon::k22x22;
			else if( size == 32 ) _size = toolkit::fltkIcon::k32x32;
			else if( size == 48 ) _size = toolkit::fltkIcon::k48x48;
			else if( size == 64 ) _size = toolkit::fltkIcon::k64x64;
			else if( size == 128 )
				_size = toolkit::fltkIcon::k128x128;
			else throw std::runtime_error( "Wrong size for icon "
				"view" );
			//
			// Now we can create our widget. We only need a basic
			// fltk::Widget for our purposes.
			//
			_widget = new iconWidget( 0, 0, 10, 10, 0 );
			_widget->box( fltk::NO_BOX );
			_widget->flags( fltk::ALIGN_CLIP );
			//
			// Now that we have a widget it's safe to bind to our
			// sources (as _update can now be called without breaking).
			//
			_binder.initialize( bind,
				boost::bind( &iconView::_update, this, _1 ), 0 );
		}
		//
		// ~iconView()
		//
		/// Destroy the icon view widget.
		//
		~iconView() { if( _widget ) delete _widget; }
		//
		// adobe::extents_t best_bounds()
		//
		/// Return the size of the icon which is going to be in here.
		///
		/// \return	the size of this icon.
		//
		adobe::extents_t best_bounds() {
			adobe::extents_t result;
			switch( _size ) {
			  case toolkit::fltkIcon::k16x16:
				result.width() = result.height() = 16;
				break;
			  case toolkit::fltkIcon::k22x22:
				result.width() = result.height() = 22;
				break;
			  case toolkit::fltkIcon::k32x32:
				result.width() = result.height() = 32;
				break;
			  case toolkit::fltkIcon::k48x48:
				result.width() = result.height() = 48;
				break;
			  case toolkit::fltkIcon::k64x64:
				result.width() = result.height() = 64;
				break;
			  case toolkit::fltkIcon::k128x128:
			  default:
				result.width() = result.height() = 128;
				break;
			}
			return result;
		}
		//
		// void place( const adobe::point_2d_t& position,
		//		const adobe::extents_t& geometry )
		//
		/// Place the widget in the given location under it's parent.
		///
		/// \param	position	the desired parent-relative
		///				position for this icon view.
		/// \param	geometry	the desired geometry for this
		///				icon view.
		//
		void place( const adobe::point_2d_t& position,
			const adobe::extents_t& geometry ) {
			assert( _widget );
			_widget->resize( position.x_m, position.y_m,
				geometry.width(), geometry.height() );
		}
		//
		// fltk::Widget* getWidget() const
		//
		/// Return the widget which draws the image for this icon for
		/// this icon view.
		///
		/// \return	the icon view widget.
		//
		fltk::Widget* getWidget() const { return _widget; }
	};

	adobe::widget_node_t icon_factory(
		const adobe::dictionary_t&	parameters,
		const adobe::widget_node_t&	parent,
		const adobe::factory_token_t&	token ) {
		//
		// Create an iconView widget, and set it to delete when the
		// assemblage is destroyed.
		//
		iconView* iv( new iconView( parameters, token, parent ) );
		token.assemblage_m.delete_on_destruction( iv );
		//
		// Insert this widget into it's parent on the main display.
		//
		fltk::Widget* w( iv->getWidget() );
		adobe::display_t::position_t display_token( adobe::insert(
			adobe::get_main_display(), parent.display_token_m,
			w ) );
		adobe::display_t::position_t overlay_token(
			token.overlay_m.insert( parent.overlay_token_m ) );
		//
		// Bind the calculate and place signals for Eve.
		//
		adobe::eve_t::signal_suite_t signals;
		signals.eve_calculate_proc_m = boost::bind(
			&eve_client::utility_widget_calculate,
			iv->best_bounds(), _1 );
		eve_client::place_func_t placer(
			boost::bind( &iconView::place, iv, _1, _2 ) );
		signals.eve_place_proc_m = boost::bind(
			&eve_client::utility_widget_place,
			placer, _1, _2,
			boost::ref( token.overlay_m ), overlay_token );
		//
		// Register the widget with the layout engine.
		//
		adobe::eve_t::iterator eve_token(
			token.layout_m.add_view_element( parent.eve_token_m,
				adobe::eve_t::insert_element_t(
					adobe::eve_t::calculate_data_t(),
					false,
					parameters,
					signals )
				)
			);
		//
		// Return the icon view.
		//
		return adobe::widget_node_t( parent.size_m, eve_token,
			display_token, overlay_token );
	}
};
